using System;
using System.Security.Cryptography;
using System.Text;

namespace Be.Timvw.Framework.Security
{
    public class HmacOneTimePassword
    {
        public const string DefaultAlgorithmName = "HMACSHA1";
        public const int DefaultNumberOfDigits = 6;

        private readonly HMAC hmac;
        private readonly int numberOfDigits;

        public HmacOneTimePassword(string secret)
        : this(DefaultAlgorithmName, secret, DefaultNumberOfDigits)
        {
        }

        public HmacOneTimePassword(string hmacAlgorithmName, string secret, int numberOfDigits)
        {
            this.hmac = HMAC.Create(hmacAlgorithmName);
            this.hmac.Key = Encoding.ASCII.GetBytes(secret);
            this.numberOfDigits = numberOfDigits;
        }

        public virtual byte[] GetHash(long iterationNumber)
        {
            byte[] text = new byte[8];
            for(int i = text.Length - 1; i >= 0; i--)
            {
                text[i] = (byte) (iterationNumber & 0xff);
                iterationNumber >>= 8;
            }

            return this.hmac.ComputeHash(text);
        }

        public virtual string GetPassword(long iterationNumber)
        {
            byte[] hash = this.GetHash(iterationNumber);

            int offset = hash[hash.Length - 1] & 0xf;

            int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);

            int otp = binary % (int) Math.Pow(10, this.numberOfDigits);
            return otp.ToString(new string('0', this.numberOfDigits));
        }
    }
}